package com.stp.rabbitmq.callback;

import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.nio.charset.StandardCharsets;

/**
 *
 * ConfirmCallback
 * 消息发送确认。这种是用来确认生产者将消息发送给交换器，交换器传递给队列的过程中，消息是否成功投递。发送确认分为两步，一是确认是否到达交换器，二是确认是否到达队列。
 * 使用该功能需要开启确认，spring-boot中配置如下：
 * spring.rabbitmq.publisher-confirms = true
 *
 * ReturnCallback
 * 通过实现ReturnCallback接口，如果消息从交换器发送到对应队列失败时触发（比如根据发送消息时指定的routingKey找不到队列时会触发）
 * 使用该功能需要开启确认，spring-boot中配置如下：
 * spring.rabbitmq.publisher-returns = true
 */
@Slf4j
@Component
public class RabbitmqCallback implements RabbitTemplate.ConfirmCallback, RabbitTemplate.ReturnCallback{
    @Autowired
    private RabbitTemplate rabbitTemplate;

    // rabbitTemplate被注入之后被调用，进行对象注入
    @PostConstruct
    public void init() {
        rabbitTemplate.setConfirmCallback(this);
        rabbitTemplate.setReturnCallback(this);
    }

    // 无论消息发送到交换机成功或者失败，都会回调该方法，前提是在application.properties中配置：spring.rabbitmq.publisher-confirm-type=correlated
    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
        try {
            String id = correlationData.getId();
            log.info("交换机接收消息反馈，其中id：%s，接收成功：%b，失败原因：%s\n", id, ack, cause);
        } catch (Exception e) {

        }
    }

    // 当交换机发送消息到队列失败的时候，将会回调该方法，成功不会回调，前提是在application.properties中配置：spring.rabbitmq.publisher-returns=true
    @Override
    public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
        log.info("交换机发送消息到队列失败，其中消息内容：%s，回复码：%d，回复内容：%s，交换机：%s，路由：%s\n", new String(message.getBody(), StandardCharsets.UTF_8), replyCode, replyText, exchange, routingKey);
    }
}
